package untils

import (
	"github.com/dgrijalva/jwt-go"
	"time"
	"net/http"
	"github.com/dgrijalva/jwt-go/request"
	"fmt"
	"github.com/gin-gonic/gin"
	"strconv"
	"strings"
	"encoding/json"
	"errors"
	"webGin/cache"
)

const Secret = "testSecret"
const Ssl int64 = 60

func CreateToken(id int64) (string, error) {
	token := jwt.New(jwt.SigningMethodHS256)
	claims := make(jwt.MapClaims)
	claims["exp"] = time.Now().Add(time.Hour * time.Duration(1)).Unix()
	claims["iat"] = time.Now().Unix()
	claims["userId"] = id
	token.Claims = claims
	return token.SignedString([]byte(Secret))
}

func ValidToken(r *http.Request) bool {
	token, err := request.ParseFromRequest(r, request.AuthorizationHeaderExtractor,
		func(token *jwt.Token) (interface{}, error) {
			return []byte(Secret), nil
		})
	if err != nil {
		return false
	}
	str := token.Signature
	v := cache.MeCache.Get(str)
	if v != nil {
		return false
	}
	if token.Valid {
		return true
	} else {
		return false
	}
}

func GetUserId(c *gin.Context) (userId int64, err error) {
	token, err := request.ParseFromRequest(c.Request, request.AuthorizationHeaderExtractor,
		func(token *jwt.Token) (interface{}, error) {
			return []byte(Secret), nil
		})

	if err != nil {
		return
	}
	if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
		uid := fmt.Sprint(claims["userId"])
		userId, err = strconv.ParseInt(uid, 10, 64)
		return
	} else {
		fmt.Println(err)
		return
	}
}

func RefreshToken(c *gin.Context) (token string, err error) {
	oldToken, err := request.ParseFromRequest(c.Request, request.AuthorizationHeaderExtractor,
		func(token *jwt.Token) (interface{}, error) {
			return []byte(Secret), nil
		})

	b := false
	if err != nil {
		if strings.EqualFold(err.Error(), "Token is expired") {
			b = true
		}
	} else {
		b = true
	}
	if b {
		str := oldToken.Signature
		s := cache.MeCache.Get(str)
		if s != nil {
			return "", errors.New("token is in blackList")
		}
		cache.MeCache.Put(str, 1, time.Duration(120*time.Second))
		oldClaims := oldToken.Claims.(jwt.MapClaims)
		n := time.Now().Unix()
		switch iat := oldClaims["iat"].(type) {
		case float64:
			bl := int64(iat) + Ssl
			if bl < n {
				return "", errors.New("token is refresh expired")
			}
		case json.Number:
			v, _ := iat.Int64()
			if v+Ssl < n {
				return "", errors.New("token is refresh expired")
			}
		}

		token := jwt.New(jwt.SigningMethodHS256)
		claims := make(jwt.MapClaims)
		claims["exp"] = time.Now().Add(time.Minute * time.Duration(1)).Unix()
		claims["iat"] = time.Now().Unix()
		claims["userId"] = oldClaims["userId"]
		token.Claims = claims
		return token.SignedString([]byte(Secret))
	}
	return
}
